针对windows平台上C++内存泄漏检测的软件和方法 | 您所在的位置:网站首页 › 内存泄露检测工具 ceph › 针对windows平台上C++内存泄漏检测的软件和方法 |
一、采用工具检测内存泄漏: 1.第一种软件:Tencent tMemMonitora.参考文档: http://www.cnblogs.com/tmemmonitor/p/4867347.html https://my.oschina.net/u/2461311/blog/519940
b.检测范围、准确性及使用性: TMM认为在进程退出时,堆内存中没有被释放且没有指针指向的无主内存块即为内存泄漏,并进而引入垃圾回收(GC, Garbage Collection)机制,在进程退出时检测出堆内存中所有没有被引用的内存单元,会二次遍历堆内存对象里的指针,对寄存器级的问题跟踪,完整扫描每个线程里32位寄存器内的指针,同时也跟踪全局数据区里的内容。 准确性来说,实际运行程序检测,每次都能检测出大量的泄漏,不过实际上能更改的、确保准确的,不一定很多,总结起来就是数量很多,但质量不保准。 使用性,总体来说还是比较简单的,直接打开软件运行生成exe文件就可以了,不过一定要按正常程序退出,不然会导致一直检测,debug代码会报错。
c.使用限制: 1.安装TMM时,用户应具有Administrator权限,并且TMM不支持中文安装路径。 2. 使用TMM时需要修改注册表,如遇安全软件弹窗警告,可将TMM加入信任列表放心使用。 3. 被检测程序不能是加壳版本,因为加壳程序的函数名和函数地址已经混淆。 4. 被检测程序需是release版本。 5. 如需在分析报告中显示泄漏点详细堆栈信息,请在被检测程序同级目录放置同版本的PDB文件,PDB解析时目录不支持中文。 6. 使用TMM导致被测程序退出时变慢属于正常情况,此时TMM正在统计内存泄漏情况,请不要手动强制结束进程。
d.使用方法: 打开软件,在软件中运行Release版本的Vanish即可。程序结束时,会自己生成报告文档,有两种一种是按照泄漏大小排序,另一种是按照泄漏次数排序。
e.使用注意事项: 1、如果有两个执行文件的名称相同,当其中一个事先运行,另一个使用工具监测启动时就会提示已经运行。 2、使用这个软件的时候一定要按照正常的流程走,添加删除进程,不然会导致这个工具一直监视这个进程,最后无法运行代码。这个非常重要!!!!
f.使用效果: 第一次检测: 按照泄漏大小排序: 检测结果: 具体省略… FINAL SUMMARY: Leaks 154 total 7887 byte(s) of leak(s) 1947878 byte(s) of unfreed
第二次检测: 按照泄漏次数排序 检测结果: 具体省略…(如想查看可以参照附件) FINAL SUMMARY: Leaks 93 total 6089 byte(s) of leak(s) 1791941 byte(s) of unfreed
2.第二种软件:LeakDiag a.参考文档: http://www.codeceo.com/article/linux-cpp-memory-overflow.html http://www.chinadmd.com/search.do?nkey=leakdiag%E4%BD%BF%E7%94%A8 b.检测范围、准确性及使用性: LeakDiag是一个监测内存泄漏的工具,可以用来精确地找到内存泄露一直到代码行。它使用微软的Detours 技术,拦截指定内存分配的调用并跟踪各种调用栈,并报告已分配但尚未释放的内存,这一信息允许让我们在排除一个内存泄露问题时,能精确查看哪些组件进行了该分配。使用正确的调试符号,我们甚至可以看见请求分配的代码行。但是在进行LOG输出(快照)的时候没有进行释放的调用栈情况。LeakDiag在start后对进程进行实时的进行监测,在输出LOG的时候,进行结果形成,可以设置定时的输出。 LeakDiag 目前支持六种类型的泄漏检查:Virtual Allocator、Heap Allocator、MPHeap Allocator、COM AllocatorCoTaskMem、COM Private Allocator、C Runtime Allocator。 准确性的话,对于我的具体使用时候发现,它的每次抓取生成xml文件少则一两百,多则四五万行的代码,人工阅读,工作量太大,也不太容易发现具体他发现了什么东西,就算是用了分析软件也只能看到每次生成Log的这个节点,使用四次左右没发现什么错误。(个人观点,也有可能是log太少,未发现) 使用性,当在登陆界面运行,没有影响,但当运行到主界面时,代码就会开始报碰到断点的错误,不过只要一直继续,程序还是可以继续运行,每次点击页面都会又有断点抛出来,十分麻烦,同时有断点触发时不能生成log,stop软件leakdiag后,程序就可以正常运行了。 c.使用方法: 参考文档:http://www.chinadmd.com/file/cwtxuzrwzi66r6ru3ietrprs_1.html http://www.cppblog.com/sandy/archive/2008/08/18/59260.html 运行程序后(程序必须在调试状态下启动,否则报错),启动LeakDiag(必须在程序启动后启动leakdiag,leakdiag不会实时刷新,否则会找不到相应程序),找到对应的程序。然后选择Windows Heap Allocator来跟踪heap的使用,按start开始,等一会按log,然后再stop会在c:\leakdiag\Logs下面生成一个log。 打开log,关键部分如下: 可以看到num = 6 的那行标注了内存泄露的源头,在mem_test.cpp 中的第12行(生成文件前,需要设置程序相应PDB位置,[Tools]-[Option]-Symbol search path): 注意:PDB路径不能包含中文字符 可以采用log分析工具LDParser,正在学习中。
d.使用注意: 对于大规模程序,勾选Resolve symbols when logging,不是很好的选择。可以在LOG完成后,再进行符号解析。因为如果勾选,在形成LOG的时候,对于大型软件需要进行大量的符号解析 f.分析结果: 1.人工读取、分析XML文档 XML文档的开始部分是模式定义(XML规范规定的)。LeakDiag产生的往往以“Save results to file d.使用结果演示:
其他几种可以检测内存泄露的软件,但并未测试成功。 1.BoundsChecker 参考文档:http://www.voidcn.com/blog/wu_123_456/article/p-2942942.html 检测范围、准确性及使用性: BoundsChecker是一个运行时错误检测工具,它主要定位程序在运行时期发生的各种错误。 BoundsChecker能检测的错误包括:指针操作和内存、资源泄露错误。内存泄露;资源泄露;对指针变量的错误操作。内存操作方面的错误。内存读、写溢出;使用未初始化的内存。API函数使用错误。 准确性不好说,找到的信息太少,而且大多是vc++环境,Vs也可以,但更少。 操作性一般,暂时运行到.exe 文件时候就会崩了,无法运行程序。 未能通过调试的原因: 在windows 7 系统下,下载运行后无法运行exe 会提示应用程序无法启动。 网络解决办法:据说是10.5版本可以在windows 7+vs2008下运行,现在用的当前版本为6.5,找到9.1版本但并没有下载成功,无法验证。
2. 使用UMDH 参考文档:http://blog.csdn.net/phiger/article/details/1932141 http://www.cnblogs.com/zhangkai87/p/3234924.html http://www.blogs8.cn/posts/EgH1zzB https://support.microsoft.com/en-us/kb/268343 检测范围、准确性及使用性: UMDH主要通过分析比较进程的Heap Stack trace信息来发现内存泄露的。可以用于分析进程的内存泄露问题。 准确度应该是还不错的,但是没有实际运行,效果不太好说,不过网上的评价还可以。 对于操作性就比较麻烦了,需要在cmd中运行,敲击代码生成log文件,会返回给你当时内存信息,就是将那个状态的内存拍下来,然后让你去分析,感觉挺专业,但是操作系数比较高。 使用方法: 1、安装 UMDH 实用程序。 2、设置系统 PATH 环境变量包含 UMDH 的安装的文件夹。 3、请将 _NT_SYMBOL_PATH 环境变量设置为 Microsoft 符号服务器的路径,以便 UMDH 可以找到调试符号文件。按以下设置,会自动下载操作系统对应的符号文件 set _NT_SYMBOL_PATH= "SRV*c:\symbols*http://msdl.microsoft.com/download/symbols;d:\myapp\bin" d:\myapp\bin为PDB所在目录 若要生成堆分配的转储文件,必须使用 Gflags.exe 实用程序,它也是包含在 Windows 调试工具产品,让操作系统知道您想要跟踪分配的内核。 gflags -i notepad.exe +ust 完成后,可以利用UMDH工作生成转储文件 Umdh -p:PID -f:"d:\old.log" Umdh -p:PID -f:"d:\new.log" umdh.exe "D:\old.log" "D:\new.log" -f:"D:\result.log"
未能通过调试的原因: ntdll.dll symbols are incorrect we must be able to see non-import symbols,根据查询应该是set _NT_SYMBOL_PATH= 这里的配置不对,不过没有找到正确的解决方法,只能暂时搁置。
三、不使用工具的内存泄漏检测方法 第一种:Visual Studio 调试器和 C 运行时 (CRT) 库参考文档:http://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html http://edu.cnzz.cn/201602/991586f9.shtml
Windows平台下面Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法,原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏。 一种Windows平台下的检测内存泄漏的方法,该方法利用_CrtDumpMemoryLeaks()函数,模仿MFC程序中默认的内存泄露检测功能,但是该方法需要修改源代码,用起来还不是很方便。
使用分析: 这种方法通过添加头文件,在程序中包括语句,在“输出”窗口中显示内存泄漏信息。 评价:如果程序总是在同一位置退出,调用语句将非常容易,也可以尝试定位内存泄漏的位置,不过要若要确定代码中某一部分是否发生了内存泄漏,可以在该部分之前和之后对内存状态拍快照,然后才能使用,也是比较麻烦的,对于了解到这部分有泄漏,然后用来特别的定位应该是可以的。
具体方法: STEP1,在程序中包括以下语句: (#include 语句必须采用上文所示顺序。 如果更改了顺序,所使用的函数可能无法正常工作。) #define _CRTDBG_MAP_ALLOC #include #include 通过包括 crtdbg.h,将 malloc 和 free 函数映射到它们的调试版本,即 _malloc_dbg 和 _free_dbg,这两个函数将跟踪内存分配和释放。 此映射只在调试版本(在其中定义了_DEBUG)中发生。 发布版本使用普通的 malloc 和 free 函数。 #define 语句将 CRT 堆函数的基版本映射到对应的“Debug”版本。 并非绝对需要该语句;但如果没有该语句,内存泄漏转储包含的有用信息将较少。 STEP2, 在添加了上述语句之后,可以通过在程序中包括以下语句(通常应恰好放在程序退出位置之前)来转储内存泄漏信息: _CrtDumpMemoryLeaks();
第二种: Windows中的Dbghelp库提供了丰富的调试API。 参考文档:http://www.cppblog.com/heath/archive/2013/11/20/203920.html
具体实现步骤: 内存泄漏检测的基本步骤是:1)包装(重载)内存分配/释放API;2)进行内存分配时记下相关信息:地址、大小、调用栈;3)释放时清除之前记录的对应信息;4)程序退出时(确保在所有内存释放操作完成之后),输出剩下的记录。其中,对进行分配操作是的调用栈回溯是个重点信息,它能够帮助我们找出内存泄漏代码。 Dbghelp库中提供了Sym*FromAddr系列API,可以通过指令地址获取函数符号,那么剩下的就是如何记录指令地址的问题了
使用分析: 具体我也去没有实现过,不过看着参考文档里,写的不是很多,但是基本都是实现的代码,评论也说他可以基本实现Vld的功能, 可以用来作为参考,实际上应该用不到。(个人观点) 第三种:内存泄漏检测项目:Visual Leak Detector 库(VLD)参考文档:https://www.cppfans.org/1381.html http://itindex.net/detail/50939-%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F http://www.cybzjx.net/article/68777.htm http://www.programgo.com/article/87891922413/
使用分析: 从查询结果上来看,这个是不使用软件来找内存泄漏中比较好的方法了,vld可以显示导致内存泄漏的完整内存分配调用堆栈。vld的检测报告能够对每个内存泄漏点提供完整的堆栈跟踪,并且包含其源文件及行号信息。 项目主页地址:http://vld.codeplex.com/,codeproject和sourceforge上都有其项目主页,大家可以自行选择下载。其中包含Windows版本和源代码版本,windows版本是头文件和lib文件,用户可以自行调用,而源码版本可以自己编译。
使用方法: 使用方法很简单,仅仅需要将它的vld.h头文件、静态链接库添加到我们的项目中即可
四、内存泄露的预防: 尽量不去手动分配内存。比如,一般不使用数组,而使用STL的vector.
2.如果需要手动分配数组,尽量使用STL中的分配方式,或者使用STL和BOOST中的智能指针。
3.某些应用,比如MSXML,尽量使用智能指针。
4.凡是使用new和delete的地方,首先注意指针的初始化,然后要注意new和delete的配对,再就是要注意错误的捕捉。很多时候,内存泄漏不是因为new和delete的配对造成的,而是在自己没有考虑到的可能结果中,程序中断而没有delete手动分配的内存.
5.类所有动态分配的成员变量,一定记得在析构函数中全部进行判断释放内存 指针容器
6.若不是在定义指针代码作用范围内,使用其它地方定义的指针时(比如全局指针,类成员变量指针),进行赋值操作的时候先判断原来指针是否有值,有则先释放原来的内存。
7.扫尾函数 有些类型对象如CDialog,CWindow,CFile,CImage等需要在Delete前做Close、Release、Destroy等操作的,Delete时检查是否已经调用了相应的扫尾函数。 这个要具体情况具体分析了,比如CDialog的子类销毁时往往需要先调用OnDestroy或是DestroyWindow,不然就可能会存在资源泄漏的问题。
8.公共模块/第三方库 公共模块一般有init()、open()和release()、terminate()、close()两种类型的函数,不要忘记扫尾类型函数的调用。
9.异常分支 若正常分支有内存需要释放,则不要忘了异常分支的内存释放如try语句的catch分支,函数中的多个return分支都要考虑到相应内存的释放。
10.动态分配对象数组: 动态分配的对象数组,记得使用delete[]来进行删除。基于两个考虑: (1)可以释放整个数组的空间; (2)调用数组中每个对象的析构函数。
11.非常规动态内存分配 不是采用常规内存分配(new、malloc、calloc、realloc)的内存也要记得释放,如strdup等。
12.单态模式 最好在程序退出时释放内存,虽然OS会回收,但对于我们以后内存泄漏检测工作能带来极大方便。
13.虚析构函数 一个类的指针被向上引用,作为基类的指针来使用的时候,把析构函数写成虚函数。这样做是为了当用一个基类的指针类型来删除一个派生类的对象时,派生 类的析构函数会被调用。(new子类的对象,删除时却采用delete父类类型的指针。new CConcreteClass的对象ptr,但delete CClass类型 的指针ptr,无法调用正确的析构函数)
14.线程的安全退出,user-interface thread安全退出
15.内存动态分配后,在各个分支路径均要考虑是否要释放掉
参考了文档:http://qiusuoge.com/9820.html http://blog.fishc.com/515.html/2 http://blog.csdn.net/wenhm/article/details/4314863
多给几个分享的文章:
文章:shared_ptr真能防止内存泄漏吗? 参考文档: http://itindex.net/detail/9508-shared_ptr-%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F
文章:C/C++内存管理详解 参考文档:http://chenqx.github.io/2014/09/25/Cpp-Memory-Management/
文章:你所知道的C++内存泄漏的各种姿势? 参考文档:https://www.zhihu.com/question/38978625
|
CopyRight 2018-2019 实验室设备网 版权所有 |